package me.seu.demo.service.netty.utils;

import lombok.extern.slf4j.Slf4j;
import me.seu.demo.service.netty.constants.ProtocolConstants;
import me.seu.demo.service.netty.message.GpsMessage;
import org.apache.commons.lang3.StringUtils;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.util.regex.Pattern;

/**
 * 网络数据获取 工具类
 *
 * @author liangfeihu
 * @number 53669
 * @since 2021/3/26 下午3:21
 */
@Slf4j
public class NetUtils {

    public static byte[] buildCommonReplyPacket(GpsMessage msg) {
        byte[] reply = new byte[10];
        reply[0] = ProtocolConstants.PACKET_START[0];
        reply[1] = ProtocolConstants.PACKET_START[1];
        reply[2] = 0x05;
        reply[3] = msg.getProtocolNo();
        byte[] packetSeqNum = msg.getPacketSeqNum();
        reply[4] = packetSeqNum[0];
        reply[5] = packetSeqNum[1];

        // 计算 “包长度”到“信息序列号”的 CRC-ITU 值
        byte[] crcData = new byte[]{0x05, msg.getProtocolNo(), packetSeqNum[0], packetSeqNum[1]};
        char crc16 = CrcItuUtils.getCrc16(crcData, crcData.length);
        byte one = (byte) ((crc16 >> 8) & 0xff);
        byte two = (byte) (crc16 & 0xff);

        reply[6] = one;
        reply[7] = two;
        reply[8] = ProtocolConstants.PACKET_END[0];
        reply[9] = ProtocolConstants.PACKET_END[1];
        return reply;
    }

    public static byte[] buildTimeCheckPacket(GpsMessage msg) {
        byte[] reply = new byte[16];
        reply[0] = ProtocolConstants.PACKET_START[0];
        reply[1] = ProtocolConstants.PACKET_START[1];
        reply[2] = 0x0B;
        reply[3] = msg.getProtocolNo();

        // 年月日， 时分秒
        byte[] dateTime = TimeUtils.getCurrentDateTime();
        reply[4] = dateTime[0];
        reply[5] = dateTime[1];
        reply[6] = dateTime[2];
        reply[7] = dateTime[3];
        reply[8] = dateTime[4];
        reply[9] = dateTime[5];

        byte[] packetSeqNum = msg.getPacketSeqNum();
        reply[10] = packetSeqNum[0];
        reply[11] = packetSeqNum[1];

        // 计算 “包长度”到“信息序列号”的 CRC-ITU 值
        byte[] crcData = new byte[]{0x0B, msg.getProtocolNo(),
                /*年月日 时分秒*/
                dateTime[0], dateTime[1], dateTime[2], dateTime[3], dateTime[4], dateTime[5]
                , packetSeqNum[0], packetSeqNum[1]};
        char crc16 = CrcItuUtils.getCrc16(crcData, crcData.length);
        byte one = (byte) ((crc16 >> 8) & 0xff);
        byte two = (byte) (crc16 & 0xff);

        reply[12] = one;
        reply[13] = two;
        reply[14] = ProtocolConstants.PACKET_END[0];
        reply[15] = ProtocolConstants.PACKET_END[1];
        return reply;
    }


    /**
     * 任意地址
     */
    public static final String ANYHOST = "0.0.0.0";

    private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$");

    /**
     * IPv4地址
     */
    public static final Pattern IPV4_PATTERN = Pattern.compile(
            "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");

    /**
     * 是否本地地址 127.x.x.x 或者 localhost
     *
     * @param host 地址
     * @return 是否本地地址
     */
    public static boolean isLocalHost(String host) {
        return StringUtils.isNotBlank(host)
                && (LOCAL_IP_PATTERN.matcher(host).matches() || "localhost".equalsIgnoreCase(host));
    }

    /**
     * 是否默认地址 0.0.0.0
     *
     * @param host 地址
     * @return 是否默认地址
     */
    public static boolean isAnyHost(String host) {
        return ANYHOST.equals(host);
    }

    /**
     * 是否IPv4地址 0.0.0.0
     *
     * @param host 地址
     * @return 是否默认地址
     */
    public static boolean isIPv4Host(String host) {
        return StringUtils.isNotBlank(host)
                && IPV4_PATTERN.matcher(host).matches();
    }

    /**
     * 是否非法地址（本地或默认）
     *
     * @param host 地址
     * @return 是否非法地址
     */
    private static boolean isInvalidLocalHost(String host) {
        return StringUtils.isBlank(host)
                || isAnyHost(host)
                || isLocalHost(host);
    }

    /**
     * 是否合法地址（非本地，非默认的IPv4地址）
     *
     * @param address InetAddress
     * @return 是否合法
     */
    private static boolean isValidAddress(InetAddress address) {
        if (address == null || address.isLoopbackAddress()) {
            return false;
        }
        String name = address.getHostAddress();
        return (name != null
                && !isAnyHost(name)
                && !isLocalHost(name)
                && isIPv4Host(name));
    }

    /**
     * 是否网卡上的地址
     *
     * @param host 地址
     * @return 是否默认地址
     */
    public static boolean isHostInNetworkCard(String host) {
        try {
            InetAddress addr = InetAddress.getByName(host);
            return NetworkInterface.getByInetAddress(addr) != null;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * InetSocketAddress转 host:port 字符串
     *
     * @param address InetSocketAddress转
     * @return host:port 字符串
     */
    public static String toAddressString(InetSocketAddress address) {
        if (address == null) {
            return StringUtils.EMPTY;
        } else {
            return toIpString(address) + ":" + address.getPort();
        }
    }

    /**
     * 得到ip地址
     *
     * @param address InetSocketAddress
     * @return ip地址
     */
    public static String toIpString(InetSocketAddress address) {
        if (address == null) {
            return null;
        } else {
            InetAddress inetAddress = address.getAddress();
            return inetAddress == null ? address.getHostName() :
                    inetAddress.getHostAddress();
        }
    }

    /**
     * 连接转字符串
     *
     * @param local1  本地地址
     * @param remote1 远程地址
     * @return
     */
    public static String channelToString(SocketAddress local1, SocketAddress remote1) {
        try {
            InetSocketAddress local = (InetSocketAddress) local1;
            InetSocketAddress remote = (InetSocketAddress) remote1;
            return toAddressString(local) + " -> " + toAddressString(remote);
        } catch (Exception e) {
            return local1 + "->" + remote1;
        }
    }

}